#!/bin/sh
#
# $Id: diskless.init 6 2009-02-19 17:43:33Z efocht $

. /etc/init.d/functions

echo "initramfs detected: init called"

mount_proc
variableize_kernel_append_parameters

##########################################################

OVL_METHOD="mount"
OVL_SKIP=","
SWAPS=
PARTITIONS=
NFSROM="dummy_string_xyz"

mount_exists() {
    local name=$1
    local mounted

    mounted=`cat /proc/mounts | cut -d " " -f 2 | egrep -c "^$name$"`
    if [ $mounted -gt 0 ]; then
	return 0
    fi
    return 1
}

OVLBASE() {
    local dir=$1

    if [ `echo $dir | grep -c "^/"` -ne 1 ]; then
	echo "ERROR: OVLBASE must be an absolute path! For example /r ..."
	shellout
    fi
    mkdir -p $dir
    OVL_BASE=$dir
}

TMPFS() {
    local mpoint=$1
    local size=$2
    local opts=$3

    if mount_exists $mpoint; then
	echo "WARNING: overlay $mpoint was already defined!"
	echo "... continuing anyway..."
    fi
    if [ ! -n "$opts" ]; then
	opts="size=$size"
    else
	opts="size=$size,$opts"
    fi
    echo "cmd:  mount tmpfs $mpoint -t tmpfs -o $opts"
    mount tmpfs $mpoint -t tmpfs -o $opts || shellout
}

nfs_mount_common() {
    local mpoint=$1
    local source=$2
    local opts=$3

    if mount_exists $mpoint; then
	echo "WARNING: overlay $mpoint was already defined!"
	echo "... continuing anyway..."
    fi
    [ -d $mpoint ] || mkdir -p $mpoint
    echo "cmd: mount $source $mpoint -t nfs -o $opts"
    mount $source $mpoint -t nfs -o $opts || shellout
}

NFSRW() {
    local mpoint=$1
    local source=$2
    local opts=$3

    if [ ! -n "$opts" ]; then
	opts="rw,nolock"
    else
	opts="rw,nolock,$opts"
    fi
    nfs_mount_common $mpoint $source $opts
}

NFSRO() {
    local mpoint=$1
    local source=$2
    local opts=$3

    if [ ! -n "$opts" ]; then
	opts="ro,nolock"
    else
	opts="ro,nolock,$opts"
    fi
    # the NFSRO mount point will be listed as comment in the fstab
    if [ "$mpoint" != "$OVL_BASE" ]; then
	NFSROM=`echo $mpoint | sed -e "s,^$OVL_BASE/,,"`
    fi
    nfs_mount_common $mpoint $source $opts
}

SWAP() {
    local dev=$1
    local init=$2
    # BE SURE that the disk device drivers are loaded at this stage,
    # otherwise enabling the swap partition at this stage will fail.
    # Nevertheless, the swap partition will be added to /etc/fstab if
    # WRITE_FSTAB is called.
    if [ -e $dev ]; then
	if [ "$init" != "noinit" ]; then
	    mkswap $dev
	fi
	swapon $dev
    fi
    SWAPS="$SWAPS $dev"
}

PARTITION() {
    local dev=$1
    local mpoint=$2
    local fs=$3
    local mopts=$4

    [ -z "$mopts" ] && mopts="defaults"
    PARTITIONS="$PARTITIONS $dev,$mpoint,$fs,$mopts"
    if [ -e $dev ]; then
	if [ `grep -c $fs /proc/filesystems` -gt 0 ]; then
	    echo "Mounting $dev on $OVL_BASE$mpoint as $fs"
	    mount -t $fs -o $mopts $dev $OVL_BASE$mpoint
	fi
    fi
}

do_overlay() {
    local src=$1
    local tgt=$2
    local tgtdir=`echo $tgt | sed -e "s,/[^/]*$,,"`

    echo "overlaying $src, using method: $OVL_METHOD"

    [ -d $tgtdir ] || mkdir -p $tgtdir 

    if [ "$OVL_METHOD" = "mount" ]; then
	if [ ! -d $tgt ]; then
	    echo "ERROR: directory $tgt not found in overlay_dir"
	    shellout
	fi
	mount --bind $src $tgt
    elif [ "$OVL_METHOD" = "link" ]; then
	local odir=`pwd`
	local i nsrc spath
	rm -rf $tgt
	cd $tgtdir
	for i in `echo $tgtdir | sed -e "s,/, ,g"` ; do
	    if [ `echo $src | grep -c -E "^$tgtdir/"` -eq 1 ]; then
		spath=`echo $src | sed -e "s,^$tgtdir/,,"`
		break
	    else
		nsrc="../$nsrc"
		tgtdir=`echo $tgtdir | sed -e "s,/[^/]*$,,"`
	    fi
	done
	if [ -n "$spath" ]; then
	    ln -s "$nsrc$spath" `basename $tgt`
	else
	    echo "Could not symlink-overlay $src to $tgt!"
	    shellout
	fi
	cd $odir
    fi
}

RETRIEVE_FILE() {
    local cfg=$1
    local tgt=$2
    local cmd dir file

    case $cfg in
	rsync:*)
	    cfg=`echo $cfg | sed -e "s,^rsync:,,"`
	    cmd="rsync -av $cfg $tgt"
	    ;;
	http:*|ftp:*)
	    cmd="wget -O $tgt $cfg"
	    ;;
	nfs:*)
	    cfg=`echo $cfg | sed -e "s,^nfs:,,"`
	    file=`basename $cfg`
	    dir=`echo $cfg | sed -e "s,/[^/]*$,,"`
	    [ -d /tmp/nfstmp ] || mkdir /tmp/nfstmp
	    mount -o ro,nolock $dir /tmp/nfstmp
	    cp /tmp/nfstmp/$file $tgt
	    umount $dir
	    ;;
	*)
	    echo "Don't know how to get $cfg"
	    shellout
	    ;;
    esac
    if [ -n "$cmd" ]; then
	echo "executing: $cmd"
	$cmd
    fi
}

EMPTYDIR() {
    local mpoint=$1
    local path=$2

    if ! mount_exists $mpoint; then
	echo "ERROR: overlay $mpoint does not exist!"
	shellout
    fi
    echo "cmd: mkdir -p $mpoint$path"
    mkdir -p $mpoint$path || shellout
    if [ ! -n "$OVL_BASE" ]; then
	echo "OVL_BASE is not defined!"
	echo "You must define it in your diskless.cfg file!"
	shellout
    fi
    OVL_SKIP="$OVL_SKIP $OVL_BASE$path ,"
    if [ "$mpoint" != "$OVL_BASE" ]; then
	# adapt permissions and ownership
	rsync -av $OVL_BASE/$path/ $mpoint/$path --exclude=*
	do_overlay $mpoint$path $OVL_BASE$path || shellout
    fi
}

COPYDIR() {
    local src=$1
    local tgt=$2
    local path=$3
    local ifempty=$4     # only copy if directory on target is empty"
    local cmd

    if ! mount_exists $src; then
	echo "ERROR: overlay $src does not exist!"
	shellout
    fi
    if ! mount_exists $tgt; then
	echo "ERROR: overlay $tgt does not exist!"
	shellout
    fi
    if [ ! -d $src$path ]; then
	echo "ERROR: directory $path not found in $OVL_BASE$src"
	shellout
    fi
    echo "cmd: mkdir -p $tgt$path (can fail if directory exists)"
    mkdir -p $tgt$path
    OVL_SKIP="$OVL_SKIP $tgt$path ,"
    if [ -n "$ifempty" ]; then
	if [ `ls -1a $tgt$path | wc -l` -gt 2 ]; then
	    echo "Directory $src$path has content, skipping copy"
	else
	    cmd="rsync -av $src$path/ $tgt$path/"
	fi
    else
	cmd="rsync -av --delete $src$path/ $tgt$path/"
    fi
    if [ -n "$cmd" ]; then
	echo "executing: $cmd"
	$cmd
    fi
    if [ ! -n "$OVL_BASE" ]; then
	echo "OVL_BASE is not defined!"
	echo "You must define it in your diskless.cfg file!"
	shellout
    fi
    if [ "$tgt" != "$OVL_BASE" ]; then
	do_overlay $tgt$path $OVL_BASE$path
    fi
}

EMBEDDIR() {
    local src=$1
    local tgt=$2
    local d odir

    echo "Embedding $src into $tgt"
    skip=" $skip "
    odir=`pwd`
    cd $src
    for d in `ls -1a | egrep -v "^(\.|\.\.)$"`; do
	[ `echo $OVL_SKIP | grep -c " $tgt/$d "` -gt 0 ] && continue
	if [ -e $tgt/$d ]; then
	    if mount_exists $tgt/$d; then
		continue
	    fi
	    if [ ! -d $tgt/$d ]; then
		continue
	    fi
	    # recurse on the directory to catch non-embeded subdirectories
	    EMBEDDIR $src/$d $tgt/$d
	else
	    do_overlay $src/$d $tgt/$d
	fi
    done
    cd $odir
}

OVERLAY_METHOD() {
    local method=$1

    case "$method" in
	link)
	    OVL_METHOD="link"
	    echo "Setting overlay method to symlink!"
	    echo "Make sure that your base overlay directory is writable!"
	    ;;
	mount)
	    OVL_METHOD="mount"
	    echo "Setting overlay method to 'mount --bind'!"
	    ;;
	unionfs)
	    OVL_METHOD="unionfs"
	    echo "Setting overlay method to 'unionfs'!"
	    ;;
	*)
	    echo "Don't know how to handle overlay method $method."
	    shellout
	    ;;
    esac
}

SWITCHROOT() {
    local mpoint=$1

    echo "Switching root to $mpoint and starting init!"
    cd $mpoint || shellout
    mount --move /proc proc
    exec switch_root -c /dev/console . /sbin/init
}

WRITE_FSTAB() {
    local root=$1
    local s p m oifs

    if [ -f $root/etc/fstab ]; then
	cp $root/etc/fstab /tmp/fstab_stub
    fi
    cat /proc/mounts | egrep " $root/| $root " | sed -e "s, $root/, /," \
	| sed -e "/ \/$NFSROM / s/^/#/" \
	| sed -e "s, $root , / ," | sed -e "s:nolock,::" > $root/etc/fstab
    cat /proc/mounts | egrep "proc|sysfs|debugfs|usbfs" >> $root/etc/fstab

    cp $root/etc/fstab /tmp/mtab

    # add swap partitions
    for s in $SWAPS; do
	echo "$s swap swap defaults 0 0" >> $root/etc/fstab
    done

    # add PARTITIONS, skip those already mounted
    for p in $PARTITIONS; do
	oifs=$IFS
	set - $p
	local dev=$1
	local mpoint=$2
	local fs=$3
	local mopts=$4
	IFS=$oifs
	if mount_exists $root$mpoint; then
	    continue
	fi
	echo "$dev $mpoint $fs $mopts 1 2" >> $root/etc/fstab
    done

    # merge with old fstab but cut out already existing entries
    for m in `cat $root/etc/fstab | cut -d " " -f 2`; do
	cat /tmp/fstab_stub | grep -v -E "[ \t]/[ \t]" > /tmp/fstab_tmp
	mv /tmp/fstab_tmp /tmp/fstab_stub
    done
    cat /tmp/fstab_stub >> $root/etc/fstab
    cp /tmp/mtab $root/etc/mtab
}

######################################
#set -x

if [ -n "$NFSMAIN" -o -n "$DISKLESSCFG" ]; then
    ifconfig_loopback
    load_my_modules
    read_kernel_append_parameters
    start_network
fi

if [ -n "$NFSROOT" ]; then
    OVLBASE /r
    NFSRW $OVL_BASE $NFSROOT rsize=8192,wsize=8192
    SWITCHROOT /r
    echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
    echo "You should not reach this point!!!"
    shellout
elif [ -n "$NFSMAIN" ]; then
    OVLBASE /r
    NFSRO $NFSMAIN $OVL_BASE rsize=8192,wsize=8192
    if [ -f $OVL_BASE/diskless.cfg ]; then
	echo "Found diskless.cfg in $OVL_BASE"
	. $OVL_BASE/diskless.cfg
    else
	echo "ERROR: expected a diskless.cfg file in the NFSMAIN directory!"
	shellout
    fi
    echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
    echo "You should not reach this point!!!"
    echo "Your diskless.cfg should end with a SWITCHROOT command!!!"
    shellout
elif [ -n "$DISKLESSCFG" ]; then
    RETRIEVE_FILE $DISKLESSCFG /tmp/diskless.cfg
    . /tmp/diskless.cfg
    echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
    echo "You should not reach this point!!!"
    echo "Your diskless.cfg should end with a SWITCHROOT command!!!"
    shellout
fi

#########################################################
# if we reached this point, we are doing a normal install
#########################################################

echo "initramfs detected: init called"

mkdir -p /new_root || shellout
mount tmpfs /new_root -t tmpfs $tmpfs_opts || shellout
cd / || shellout
cp -a `/bin/ls | grep -v new_root` /new_root/ || shellout
cd /new_root || shellout
mkdir -p old_root || shellout

mount --move .  / && exec chroot . /linuxrc
